'use strict';

const Service = require('egg').Service;

class CacheService extends Service {
  ////// Attributes /////////////////////////////////////////////

  constructor(ctx) {
    super(ctx);
    // Public
    this.settings = {};
    this.settingsCount;
    this.userGroup = new Array();
    this.categories = new Array();
    this.smilies = new Array();
    this.wordFilter = new Array();
    this.categoriesCount;
    this.calendarHTML;
    this.recentArticles = new Array();
    this.recentComments = new Array();
    this.loaded = false;
    this.articleMode = ['', '', ctx.__('draft'), ctx.__('hidden'), ctx.__('private')];
  }

  ////// Methods ////////////////////////////////////////////////

  // Load All Caches --------------------------
  async load() {
    if (this.ctx.app.cacheSettings !== undefined) {
      this.fillSettings(this.ctx.app.cacheSettings);
    } else {
      await this.loadSettings();
    }
    if (this.ctx.app.cacheCategories !== undefined) {
      this.fillCategories(this.ctx.app.cacheCategories);
    } else {
      await this.loadCategories();
    }
    if (this.ctx.app.cacheUserGroup !== undefined) {
      this.fillUserGroup(this.ctx.app.cacheUserGroup);
    } else {
      await this.loadUserGroup();
    }
    if (this.ctx.app.cacheSmilies !== undefined) {
      this.fillSmilies(this.ctx.app.cacheSmilies);
    } else {
      await this.loadSmilies();
    }
    if (this.ctx.app.cacheWordFilter !== undefined) {
      this.fillWordFilter(this.ctx.app.cacheWordFilter);
    } else {
      await this.loadWordFilter();
    }
    if (this.ctx.app.cacheCalendar !== undefined &&
      this.ctx.app.cacheCalendarDay == (new Date()).getDate()) {
      this.calendarHTML = this.ctx.app.cacheCalendar;
    } else {
      await this.loadCalendar();
    }
    if (this.ctx.app.cacheArticles !== undefined) {
      this.fillArticles(this.ctx.app.cacheArticles);
    } else {
      await this.loadArticles();
    }
    if (this.ctx.app.cacheComments !== undefined) {
      this.fillComments(this.ctx.app.cacheComments);
    } else {
      await this.loadComments();
    }
    this.loaded = true;
  }

  // Load All From DB --------------------------
  async loadFromDB() {
    await this.loadSettings();
    await this.loadCategories();
    await this.loadUserGroup();
    await this.loadSmilies();
    await this.loadCalendar();
    await this.loadArticles();
    await this.loadComments();

    this.loaded = true;
  }

  // Load Settings ----------------------------
  async loadSettings() {
    this.settings = new Array();
    await this.saveToCache('cacheSettings', null);
    var tmpA = await this.ctx.app.connBlog.query('SELECT * FROM blog_settings', undefined,
      undefined, true);
    if (tmpA != null && tmpA.length) {
        await this.saveToCache('cacheSettings', tmpA);
        this.fillSettings(tmpA);
    }

  }

  // Fill Settings ----------------------------
  fillSettings(settings) {
    if (settings == null) return;
    this.settingsCount = settings.length;
    for (var i = 0; i < settings.length; i++) {
      this.settings[settings[i].set_name] = (settings[i].set_type == 0) ? settings[i].set_value0 : settings[i].set_value1;
    }
  }

  // Load Categories ----------------------------
  async loadCategories() {
    this.categories = new Array();
    this.saveToCache('cacheCategories', null);
    // Don't change the order of the fields in SQL string
    var tmpA = await this.ctx.app.connBlog.query(
      'SELECT cat_ID,cat_Name,cat_Order,cat_articleCount,cat_hidden,cat_Locked FROM blog_category ORDER BY cat_Order ASC',
      undefined, undefined, true);
    if (tmpA != null) {
        this.saveToCache('cacheCategories', tmpA);
        this.fillCategories(tmpA);
    }

  }

  // Fill Categories ----------------------------
  async fillCategories(arr) {
    if (arr == null) return;
    for (var i = 0; i < arr.length; i++) {
      this.categories[i] = {
        'id': arr[i].cat_ID,
        'name': arr[i].cat_Name,
        'order': arr[i].cat_Order,
        'articleCount': arr[i].cat_articleCount,
        'hidden': arr[i].cat_hidden,
        'locked': arr[i].cat_Locked
      };
    }
  }

  // Get Category by ID ----------------------------
  async getCategoryByID(intID) {
    for (var i in this.categories) {
      if (this.categories[i].id == intID) {
        return this.categories[i];
      }
    }
    return {
      'id': 0,
      'name': '',
      'articleCount': 0,
      'hidden': true,
      'locked': true,
    };
  }

  // Load User Group Info ----------------------------
  async loadUserGroup() {
    this.userGroup = new Array();
    await this.saveToCache('cacheUserGroup', null);
    // Don't change the order of the fields in SQL string
    var tmpA = await this.ctx.app.connBlog.query(
      'SELECT group_ID,group_Name,group_Rights FROM blog_usergroup',
      undefined, undefined, true);
    if (tmpA != null) {
        await this.saveToCache('cacheUserGroup', tmpA);
        await this.fillUserGroup(tmpA);
    }

  }

  // Fill User Group ----------------------------
  fillUserGroup(arr) {
    if (arr == null) return;
    for (var i = 0; i < arr.length; i++) {
      var strRights = arr[i].group_Rights;
      var arrRights = new Array();
      for (var j = 0; j < strRights.length; j++) {
        arrRights[j] = parseInt(strRights.charAt(j), 10);
      }
      this.userGroup[i] = {
        'id': arr[i].group_ID,
        'name': arr[i].group_Name,
        'rights': {
          'view': arrRights[0],
          'post': arrRights[1],
          'edit': arrRights[2],
          'delete': arrRights[3],
          'upload': arrRights[4],
        },
      };
    }
  }

  // Load Smilies Data----------------------------
  async loadSmilies() {
    this.smilies = new Array();
    await this.saveToCache('cacheSmilies', null);
    // Don't change the order of the fields in SQL string
    var tmpA = await this.ctx.app.connBlog.query(
      'SELECT sm_id,sm_code,sm_image FROM blog_smilies', undefined, undefined,
      true);
    if (tmpA != null) {
        await this.saveToCache('cacheSmilies', tmpA);
        this.fillSmilies(tmpA);
    }

  }

  // Fill Smilies Array ----------------------------
  fillSmilies(arr) {
    if (arr == null) return;
    for (var i = 0; i < arr.length; i++) {
      this.smilies[i] = {
        'id': arr[i].sm_id,
        'code': arr[i].sm_code,
        'image': arr[i].sm_image,
      };
    }
  }

  // Load Word Filter List Data----------------------------
  async loadWordFilter() {
    this.wordFilter = new Array();
    await this.saveToCache('cacheWordFilter', null);
    // Don't change the order of the fields in SQL string
    var tmpA = await this.ctx.app.connBlog.query(
      'SELECT wf_id,wf_mode,wf_text,wf_replace,wf_regExp FROM blog_wordfilter',
      undefined, undefined, true);
    if (tmpA != null) {
        await this.saveToCache('cacheWordFilter', tmpA);
        this.fillWordFilter(tmpA);
    }

  }

  // Fill Word Filter List ----------------------------
  fillWordFilter(arr) {
    if (arr == null) return;
    for (var i = 0; i < arr.length; i++) {
      this.wordFilter[i] = {
        'id': arr[i].wf_id,
        'mode': arr[i].wf_mode,
        'text': arr[i].wf_text,
        'replace': arr[i].wf_replace,
        'isRegExp': arr[i].wf_regExp,
      };
    }
  }

  // Load Recent Articles----------------------------
  async loadArticles() {
    this.recentArticles = new Array();
    await this.saveToCache('cacheArticles', null);
    var intCount = this.settings['recentArticleList'];
    if (this.ctx.helper.checkInt(intCount) < 10) intCount = 10;
    if (this.ctx.helper.checkInt(intCount) <
      this.settings['articlePerPageNormal']) intCount = this.settings['articlePerPageNormal'];

    // Get a list of articles we need to cache
    var arrList = new Array();

    // Exclude articles in hidden category
    var sqlWHERE = '';
    for (var i = 0; i < this.categories.length; i++) {
      if (this.categories[i].hidden) sqlWHERE += ' AND log_catid<>' +
        this.categories[i].id;
    }

    var tmpA = await this.ctx.app.connBlog.query('SELECT log_id FROM blog_article tLog WHERE log_mode=1 ' + sqlWHERE +
      ' ORDER BY log_postTime DESC', intCount);
    if (tmpA && tmpA.length) {
      for (var i = 0; i < tmpA.length; i++) {
        tmpA[i]['log_id'] && arrList.push(tmpA[i]['log_id']);
      }
    }

    tmpA = await this.ctx.app.connBlog.query('SELECT log_id FROM blog_article ORDER BY log_postTime DESC', intCount);
    if (tmpA && tmpA.length) {
      for (var i = 0; i < tmpA.length; i++) {
        tmpA[i]['log_id'] && arrList.push(tmpA[i]['log_id']);
      }
    }

    // Remove duplicated items
    arrList = arrList.join(',');

    // Load articles from DB
    if (arrList != '') {
      // Get all article that later than that time
      // Don't change the order of the fields in SQL string
      tmpA = await this.ctx.app.connBlog.query(
        'SELECT * FROM blog_article WHERE log_id IN (' +
        arrList + ') ORDER BY log_postTime DESC', undefined, undefined, true);
      if (tmpA && tmpA.length) {
          await this.saveToCache('cacheArticles', tmpA);
          this.fillArticles(tmpA);
      }

    }
  }

  // Fill Recent Articles Array ----------------------------
  fillArticles(arr) {
    for (var i = 0; i < arr.length; i++) {
      this.recentArticles[i] = {
        'log_id': arr[i].log_id,
        'log_catID': arr[i].log_catID,
        'log_authorID': arr[i].log_authorID,
        'log_author': arr[i].log_author,
        'log_title': arr[i].log_title,
        'log_content0': arr[i].log_content0,
        'log_content1': arr[i].log_content1,
        'log_editMark': arr[i].log_editMark,
        'log_mode': arr[i].log_authorID,
        'log_locked': arr[i].log_locked,
        'log_selected': arr[i].log_selected,
        'log_ubbFlags': arr[i].log_ubbFlags,
        'log_trackbackURL': arr[i].log_trackbackURL,
        'log_postTime': arr[i].log_postTime,
        'log_ip': arr[i].log_ip,
        'log_commentCount': arr[i].log_commentCount,
        'log_viewCount': arr[i].log_viewCount,
        'log_trackbackCount': arr[i].log_trackbackCount,
      };
    }
  }

  // Get Article by ID
  getArticleByID(intID) {
    for (var i = 0; i < this.recentArticles.length; i++) {
      if (this.recentArticles[i]['log_id'] == intID) {
        return this.recentArticles[i];
      }
    }
    return false;
  }

  // Load Recent Comments----------------------------
  async loadComments() {
    this.recentComments = new Array();
    await this.saveToCache('cacheComments', 'none');
    var intCount = this.settings['recentCommentList'];
    if (this.ctx.helper.checkInt(intCount) < 10) intCount = 10;
    // Get a list of comments we need to cache
    var arrList = new Array();
    var tmpA = await this.ctx.app.connBlog.query('SELECT tComm.comm_id FROM blog_article tLog, blog_comment tComm, blog_category tCat WHERE tLog.log_id=tComm.log_id AND tLog.log_catid=tCat.cat_id AND tLog.log_mode=1 AND tCat.cat_hidden=FALSE ORDER BY tComm.comm_postTime DESC', intCount);
    if (tmpA) {
      for (var i = 0; i < tmpA.length; i++) {
        arrList = arrList.concat(tmpA[i]['comm_id']);
      }
    }

    tmpA = await this.ctx.app.connBlog.query('SELECT comm_id FROM blog_comment ORDER BY comm_postTime DESC', intCount);
    if (tmpA) {
      for (var i = 0; i < tmpA.length; i++) {
        arrList = arrList.concat(tmpA[i]['comm_id']);
      }
    }

    // Remove duplicated items
    arrList = arrList.sort();
    var arrItem = arrList[0];
    for (var i = 1; i < arrList.length; i++) {
      if (arrItem == arrList[i]) {
        arrList.splice(i, 1);
        i--;
      } else {
        arrItem = arrList[i];
      }
    }
    arrList = arrList.join(',');

    if (arrList != '') {
      // Don't change the order of the fields in SQL string
      var tmpA = await this.ctx.app.connBlog.query(
        'SELECT tComm.comm_id, tComm.log_id, tComm.comm_author, tComm.comm_content, tComm.comm_hidden, tComm.comm_postTime, tLog.log_title, tLog.log_mode, tCat.cat_hidden, tComm.comm_ubbFlags FROM blog_article tLog, blog_comment tComm, blog_category tCat WHERE tLog.log_id=tComm.log_id AND tLog.log_catid=tCat.cat_id AND tComm.comm_id IN (' +
        arrList + ') ORDER BY tComm.comm_postTime DESC', undefined, undefined,
        true);
      if (tmpA != null) {
        if (tmpA.ubound(2) > -1) {
          await this.saveToCache('cacheComments', tmpA);
          this.fillComments(tmpA);
        }
      }

    }
  }

  // Fill Recent Comments Array ----------------------------
  fillComments(arr) {
    if (arr == 'none') return;
    var count = arr.ubound(2);
    for (var i = 0; i <= count; i++) {
      this.recentComments[i] = {
        'id': arr.getItem(0, i),
        'log_id': arr.getItem(1, i),
        'author': arr.getItem(2, i),
        'content': arr.getItem(3, i),
        'hidden': arr.getItem(4, i),
        'postTime': new Date(arr.getItem(5, i)),
        'log_title': arr.getItem(6, i),
        'log_mode': arr.getItem(7, i),
        'cat_hidden': arr.getItem(8, i),
        'ubbFlags': arr.getItem(9, i),
      };
    }
  }

  // Load Calendar
  async loadCalendar() {
    this.calendarHTML = await this.generateCalendar();
    await this.saveToCache('cacheCalendar', this.calendarHTML);
    await this.saveToCache('cacheCalendarDay', (new Date()).getDate());
  }

  // Generate Calendar ------------------------------
  async generateCalendar(intYear, intMonth) {
    var tmpDate = new Date();
    if (isNaN(intYear) || intYear == undefined || intYear == 0) {
      intYear = tmpDate.getFullYear();
    }
    if (isNaN(intMonth) || intMonth == undefined || intMonth == 0) {
      intMonth = tmpDate.getMonth() + 1;
    }
    let intToday = tmpDate.getDate();
    let currentYear = tmpDate.getFullYear();
    let currentMonth = tmpDate.getMonth() + 1;

    var dayInMonth = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    if ((intYear % 4 == 0 && intYear % 100 != 0) ||
      (intYear % 100 == 0 && (intYear / 100) % 4 == 0)) {
      dayInMonth[2] = 29;
    }

    var prevMonth = (intMonth - 1) == 0 ? 12 : intMonth - 1;
    var nextMonth = (intMonth + 1) == 13 ? 1 : intMonth + 1;
    var prevYear = prevMonth == 12 ? intYear - 1 : intYear;
    var nextYear = nextMonth == 1 ? intYear + 1 : intYear;

    tmpDate.setFullYear(intYear, intMonth - 1, 1);
    var startWeekDay = tmpDate.getDay();

    var arrEvent = new Array();
    var tmpA = await this.ctx.app.connBlog.query(
      'SELECT log_Title,log_postTime,log_mode FROM blog_article WHERE YEAR(log_PostTime)=' +
      intYear + ' AND MONTH(log_postTime)=' + intMonth +
      ' ORDER BY log_PostTime ASC', 1, 1);
    if (tmpA && tmpA.length) {
      for (var i = 0; i < tmpA.length; i++) {
        var tag = (new Date(tmpA[i]['log_postTime'])).getDate();
        if (arrEvent[tag] == undefined) arrEvent[tag] = '';
        arrEvent[tag] += '- ' + (tmpA[i]['log_mode'] > 2 ?
          tmpA[i]['log_title'].replace(/./g, '*') :
          this.ctx.helper.HTMLEncodeLite(tmpA[i]['log_title'])) + '\n';
      }
    }
    tmpA = /10f1ad1cc06f152380e5a6c39a33d6943fafd7da/;

    var output = '<table cellspacing="1" width="100%" id="calendar">\n';
    output += '<tr><td colspan="7" class="calendar-top">';
    output += '\t<a href="default.asp?date=' + (intYear - 1) + '-' + intMonth +
      '">&laquo;</a>\n';
    output += '\t<a href="default.asp?date=' + intYear + '-' + intMonth +
      '"><span class="calendar-year">' + intYear + '</span></a>\n';
    output += '\t<a href="default.asp?date=' + (intYear + 1) + '-' + intMonth +
      '">&raquo;</a>\n';
    output += '\t&nbsp;&nbsp;\n';
    output += '\t<a href="default.asp?date=' + prevYear + '-' + prevMonth +
      '">&laquo;</a>\n';
    output += '\t<a href="default.asp?date=' + intYear + '-' + intMonth +
      '"><span class="calendar-month">' + this.ctx.__('month_' + intMonth) +
      '</span></a>\n';
    output += '\t<a href="default.asp?date=' + nextYear + '-' + nextMonth +
      '">&raquo;</a>\n';
    output += '</td></tr>\n';
    output += '<tr class="calendar-weekdays">\n';
    for (var i = 0; i < 7; i++) {
      output += '\t<td class="calendar-weekday-cell">' +
        this.ctx.__('weekday_abbr_' + i) + '</td>\n';
    }
    output += '</tr>\n';
    if (startWeekDay > 0) output += '<tr>\n';
    for (var i = 0; i < startWeekDay; i++) {
      output += '\t<td class="calendar-day-blank"></td>\n';
    }
    for (var i = 1; i <= dayInMonth[intMonth]; i++) {
      var theDay = (i + startWeekDay) % 7;
      var styleClass = 'calendar-day';
      if (theDay == 0) styleClass = 'calendar-saturday';
      if (theDay == 1) {
        output += '<tr>\n';
        styleClass = 'calendar-sunday';
      }
      if (intYear == currentYear && intMonth == currentMonth && i ==
        intToday) styleClass = 'calendar-today';
      output += '\t<td class="' + styleClass + '">';
      output += arrEvent[i] ?
        '<a href="default.asp?date=' + intYear + '-' + intMonth + '-' + i +
        '" class="calendar" title="' +
        arrEvent[i].substr(0, arrEvent[i].length - 1) + '">' + i + '</a>' :
        i;
      output += '</td>\n';
      if (theDay == 0) {
        output += '</tr>\n';
      }
    }
    var sufBlank = 7 - (startWeekDay + dayInMonth[intMonth]) % 7;
    if (sufBlank != 0 && sufBlank != 7) {
      for (var i = 0; i < sufBlank; i++) {
        output += '\t<td class="calendar-day-blank"></td>\n';
      }
      output += '</tr>\n';
    }
    output += '</table>\n';

    return output;

  }

  /////////// Update Routines Below ////////////

  // Update for Article Counter
  async updateArticleCounter(id, strName, tValue) {
    if (this.ctx.app.cacheArticles === undefined) return;
    var intName;
    switch (strName) {
      case 'comment':
        intName = 15;
        break;
      case 'view':
        intName = 16;
        break;
      case 'trackback':
        intName = 17;
        break;
    }

    // this.ctx.app.cacheArticles =

    //Application('cacheArticles') = updateVBArray(
    //  Application('cacheArticles'), id, intName, tValue, false);

  }

  // Update for Global Counter
  async updateGlobalCounter(id, tValue) {
    if (this.ctx.app.cacheSettings == null) return;
    //Application('cacheSettings') = updateVBArray(
    //  Application('cacheSettings'), id, 2, tValue, false);

  }

  /////////// General Routines Below ////////////

  // Save to Cache
  async saveToCache(strName, obj) {
      this.ctx.app.cache(strName, obj);
  }

  // Delete from Cache
  async deleteFromCache(strName) {
    this.ctx.app.cache(strName, null);
  }

  // Clean up caches 按指定前缀清除缓存
  async clean(key) {
    throw new Error('not implemented.');
  }

  async cleanAll() {
    this.ctx.app.cache(null);
  }
}

module.exports = CacheService;
